package com.yutel.comp.redis;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPool;

public class JedisUtil {
	private static final Logger log = LoggerFactory.getLogger(JedisUtil.class);
	private static Map<String, JedisPool> maps = new HashMap<String, JedisPool>();
	private static ShardedJedisPool shardedJedisPool;

	private static int maxidle = 30000;
	private static int timeout = 1000;
	private static int retryNum = 3;

	private JedisUtil() {
	}

	private static ShardedJedisPool getShardedPool(String ip, int port) {
		if (shardedJedisPool == null) {
			JedisPoolConfig config = new JedisPoolConfig();
			config.setMaxIdle(maxidle);
			config.setTestOnBorrow(false);
			List<JedisShardInfo> shards = new ArrayList<JedisShardInfo>();
			shards.add(new JedisShardInfo(ip, port, "master"));
			shardedJedisPool = new ShardedJedisPool(config, shards);
		}
		return shardedJedisPool;
	}

	/**
	 * 获取连接池.
	 * 
	 * @return 连接池实例
	 */
	private static JedisPool getPool(String ip, int port) {
		String key = ip + ":" + port;
		JedisPool pool = null;
		if (!maps.containsKey(key)) {
			JedisPoolConfig config = new JedisPoolConfig();
			config.setMaxIdle(maxidle);
			config.setTestOnBorrow(true);
			config.setTestOnReturn(true);
			try {
				pool = new JedisPool(config, ip, port, timeout);
				maps.put(key, pool);
			} catch (Exception e) {
				e.printStackTrace();
			}
		} else {
			pool = maps.get(key);
		}
		return pool;
	}

	/**
	 * 类级的内部类，也就是静态的成员式内部类，该内部类的实例与外部类的实例 没有绑定关系，而且只有被调用到时才会装载，从而实现了延迟加载。
	 */
	private static class RedisUtilHolder {
		private static JedisUtil instance = new JedisUtil();
	}

	/**
	 * 当getInstance方法第一次被调用的时候，它第一次读取
	 * RedisUtilHolder.instance，导致RedisUtilHolder类得到初始化；而这个类在装载并被初始化的时候，会初始化它的静
	 * 态域，从而创建RedisUtil的实例，由于是静态的域，因此只会在虚拟机装载类的时候初始化一次，并由虚拟机来保证它的线程安全性。
	 * 这个模式的优势在于，getInstance方法并没有被同步，并且只是执行一个域的访问，因此延迟初始化并没有增加任何访问成本。
	 */
	public static JedisUtil getInstance() {
		return RedisUtilHolder.instance;
	}

	/**
	 * 获取Redis实例.
	 * 
	 * @return Redis工具类实例
	 */
	public Jedis getJedis(String ip, int port) {
		Jedis jedis = null;
		int count = 0;
		do {
			try {
				jedis = getPool(ip, port).getResource();
			} catch (Exception e) {
				log.error("get redis master1 failed!", e);
				if (jedis != null) {
					jedis.close();
				}
			}
			count++;
		} while (jedis == null && count < retryNum);
		return jedis;
	}

	public ShardedJedis getShardedJedis(String ip, int port) {
		ShardedJedis jedis = null;
		int count = 0;
		do {
			try {
				jedis = getShardedPool(ip, port).getResource();
			} catch (Exception e) {
				log.error("get sharded redis master1 failed!", e);
				if (jedis != null) {
					jedis.close();
				}
			}
			count++;
		} while (jedis == null && count < retryNum);
		return jedis;
	}

	/**
	 * 释放redis实例到连接池.
	 * 
	 * @param jedis
	 *            redis实例
	 */
	@SuppressWarnings("deprecation")
	public void closeJedis(Jedis jedis, String ip, int port) {
		if (jedis != null) {
			getPool(ip, port).returnResource(jedis);
		}
	}

}
